/*
**
** File: ymdeltat.c
**
** YAMAHA DELTA-T adpcm sound emulation subroutine
** used by fmopl.c(v0.36e-) and fm.c(v0.36c-)
**
** Base program is YM2610 emulator by Hiromitsu Shioya.
** Written by Tatsuyuki Satoh
**
** Version 0.36b
**
** sound chips who has this unit
**
** YM2608   OPNA
** YM2610/B OPNB
** Y8950    MSX AUDIO
**
**
*/

#ifndef YM_INLINE_BLOCK

// #include "neo_snd.h"
#include "fm.h"
#include "ymdeltat.h"
//#include "../state.h"

u8 *ym_deltat_memory;	/* memory pointer */

/* Forecast to next Forecast (rate = *8) */
/* 1/8 , 3/8 , 5/8 , 7/8 , 9/8 , 11/8 , 13/8 , 15/8 */
const s32 ym_deltat_decode_tableB1[16] = {
  1, 3, 5, 7, 9, 11, 13, 15,
  -1, -3, -5, -7, -9, -11, -13, -15,
};

/* delta to next delta (rate= *64) */
/* 0.9 , 0.9 , 0.9 , 0.9 , 1.2 , 1.6 , 2.0 , 2.4 */
const s32 ym_deltat_decode_tableB2[16] = {
  57, 57, 57, 57, 77, 102, 128, 153,
  57, 57, 57, 57, 77, 102, 128, 153
};

/* DELTA-T-ADPCM write register */
void
YM_DELTAT_ADPCM_Write (YM_DELTAT * DELTAT, int r, int v)
{
  if (r >= 0x10)
    return;
  DELTAT->reg[r] = v;		/* stock data */

  switch (r)
    {
    case 0x00:			/* START,REC,MEMDATA,REPEAT,SPOFF,--,--,RESET */
#if 0
    case 0x60:			/* write buffer MEMORY from PCM data port */
    case 0x20:			/* read  buffer MEMORY to   PCM data port */
#endif
      if (v & 0x80)
	{
	  DELTAT->portstate = v & 0x90;	/* start req,memory mode,repeat flag copy */
			/**** start ADPCM ****/
	  DELTAT->volume_w_step =
	    (double) DELTAT->volume * DELTAT->step / (1 << YM_DELTAT_SHIFT);
	  DELTAT->now_addr = (DELTAT->start) << 1;
	  DELTAT->now_step = (1 << YM_DELTAT_SHIFT) - DELTAT->step;
	  /*adpcm->adpcmm   = 0; */
	  DELTAT->adpcmx = 0;
	  DELTAT->adpcml = 0;
	  DELTAT->adpcmd = YM_DELTAT_DELTA_DEF;
	  DELTAT->next_leveling = 0;
	  DELTAT->flag = 1;	/* start ADPCM */

	  if (!DELTAT->step)
	    {
	      DELTAT->flag = 0;
	      DELTAT->portstate = 0x00;
	    }
			/**** PCM memory check & limit check ****/
	  if (DELTAT->memory == 0)
	    {			/* Check memory Mapped */
	      /*Log(LOG_ERR,"YM Delta-T ADPCM rom not mapped\n"); */
	      DELTAT->flag = 0;
	      DELTAT->portstate = 0x00;
	      /*logerror("DELTAT memory 0\n"); */
	    }
	  else
	    {
	      if (DELTAT->end >= DELTAT->memory_size)
		{		/* Check End in Range */
		  /*Log(LOG_ERR,"YM Delta-T ADPCM end out of range: $%08x\n",DELTAT->end); */
		  DELTAT->end = DELTAT->memory_size - 1;
		  /*logerror("DELTAT end over\n"); */
		}
	      if (DELTAT->start >= DELTAT->memory_size)
		{		/* Check Start in Range */
		  /*Log(LOG_ERR,"YM Delta-T ADPCM start out of range: $%08x\n",DELTAT->start); */
		  DELTAT->flag = 0;
		  DELTAT->portstate = 0x00;
		  /*logerror("DELTAT start under\n"); */
		}
	    }
	}
      else if (v & 0x01)
	{
	  DELTAT->flag = 0;
	  DELTAT->portstate = 0x00;
	}
      break;
    case 0x01:			/* L,R,-,-,SAMPLE,DA/AD,RAMTYPE,ROM */
      DELTAT->portcontrol = v & 0xff;
      DELTAT->pan = &DELTAT->output_pointer[(v >> 6) & 0x03];
      break;
    case 0x02:			/* Start Address L */
    case 0x03:			/* Start Address H */
      DELTAT->start =
	(DELTAT->reg[0x3] * 0x0100 | DELTAT->reg[0x2]) << DELTAT->portshift;
      break;
    case 0x04:			/* Stop Address L */
    case 0x05:			/* Stop Address H */
      DELTAT->end =
	(DELTAT->reg[0x5] * 0x0100 | DELTAT->reg[0x4]) << DELTAT->portshift;
      DELTAT->end += (1 << DELTAT->portshift) - 1;
      break;
    case 0x06:			/* Prescale L (PCM and Recoard frq) */
    case 0x07:			/* Proscale H */
    case 0x08:			/* ADPCM data */
      break;
    case 0x09:			/* DELTA-N L (ADPCM Playback Prescaler) */
    case 0x0a:			/* DELTA-N H */
      DELTAT->delta = (DELTAT->reg[0xa] * 0x0100 | DELTAT->reg[0x9]);
      DELTAT->step =
	(u32) ((double)
		  (DELTAT->delta * (1 << (YM_DELTAT_SHIFT - 16))) *
		  (DELTAT->freqbase));
      DELTAT->volume_w_step =
	(double) DELTAT->volume * DELTAT->step / (1 << YM_DELTAT_SHIFT);
      break;
    case 0x0b:			/* Level control (volume , voltage flat) */
      {
	s32 oldvol = DELTAT->volume;
	DELTAT->volume =
	  (v & 0xff) * (DELTAT->output_range / 256) / YM_DELTAT_DECODE_RANGE;
	if (oldvol != 0)
	  {
	    DELTAT->adpcml =
	      (int) ((double) DELTAT->adpcml / (double) oldvol *
		     (double) DELTAT->volume);
	    DELTAT->sample_step =
	      (int) ((double) DELTAT->sample_step / (double) oldvol *
		     (double) DELTAT->volume);
	  }
	DELTAT->volume_w_step =
	  (int) ((double) DELTAT->volume * (double) DELTAT->step /
		 (double) (1 << YM_DELTAT_SHIFT));
      }
      break;
    }
}

void
YM_DELTAT_ADPCM_Reset (YM_DELTAT * DELTAT, int pan)
{
  DELTAT->now_addr = 0;
  DELTAT->now_step = 0;
  DELTAT->step = 0;
  DELTAT->start = 0;
  DELTAT->end = 0;
  /* F2610->adpcm[i].delta     = 21866; */
  DELTAT->volume = 0;
  DELTAT->pan = &DELTAT->output_pointer[pan];
  /* DELTAT->flagMask  = 0; */
  DELTAT->arrivedFlag = 0;
  DELTAT->flag = 0;
  DELTAT->adpcmx = 0;
  DELTAT->adpcmd = 127;
  DELTAT->adpcml = 0;
  /*DELTAT->adpcmm    = 0; */
  DELTAT->volume_w_step = 0;
  DELTAT->next_leveling = 0;
  DELTAT->portstate = 0;
  /* DELTAT->portshift = 8; */
}

void
YM_DELTAT_postload (YM_DELTAT * DELTAT, u8 * regs)
{
  int r;

  /* to keep adpcml and resample_step */
  DELTAT->volume = 0;
  /* update */
  for (r = 1; r < 16; r++)
    YM_DELTAT_ADPCM_Write (DELTAT, r, regs[r]);
  DELTAT->reg[0] = regs[0];
  /* current rom data */
  DELTAT->now_data = *(ym_deltat_memory + (DELTAT->now_addr >> 1));

}

void
adpcmb_post_load (void)
{
  YM2610 *F2610 = &(FM2610);
  YM_DELTAT_postload (&F2610->deltaT, &F2610->REGS[0x100]);
}

void
YM_DELTAT_savestate (YM_DELTAT * DELTAT)
{
#if 0
  create_state_register (ST_YM2610_ADPCMB, "DT.portstate", 1,
			 &DELTAT->portstate, 1 * sizeof (u8), REG_u8);
  create_state_register (ST_YM2610_ADPCMB, "DT.address", 1, &DELTAT->now_addr,
			 1 * sizeof (u32), REG_u32);
  create_state_register (ST_YM2610_ADPCMB, "DT.step", 1, &DELTAT->now_step,
			 1 * sizeof (u32), REG_u32);
  create_state_register (ST_YM2610_ADPCMB, "DT.adpcmx", 1, &DELTAT->adpcmx,
			 1 * sizeof (s32), REG_INT32);
  create_state_register (ST_YM2610_ADPCMB, "DT.adpcmd", 1, &DELTAT->adpcmd,
			 1 * sizeof (s32), REG_INT32);
  create_state_register (ST_YM2610_ADPCMB, "DT.adpcml", 1, &DELTAT->adpcml,
			 1 * sizeof (s32), REG_INT32);
  create_state_register (ST_YM2610_ADPCMB, "DT.next_leveling", 1,
			 &DELTAT->next_leveling, 1 * sizeof (s32),
			 REG_INT32);
  create_state_register (ST_YM2610_ADPCMB, "DT.sample_step", 1,
			 &DELTAT->sample_step, 1 * sizeof (s32),
			 REG_INT32);
  set_post_load_function (ST_YM2610_ADPCMB, adpcmb_post_load);
#endif
}

//#else /* YM_INLINE_BLOCK */

/* ---------- inline block ---------- */

/* DELTA-T particle adjuster */
/*
#define YM_DELTAT_DELTA_MAX (24576)
#define YM_DELTAT_DELTA_MIN (127)
#define YM_DELTAT_DELTA_DEF (127)

#define YM_DELTAT_DECODE_RANGE 32768
#define YM_DELTAT_DECODE_MIN (-(YM_DELTAT_DECODE_RANGE))
#define YM_DELTAT_DECODE_MAX ((YM_DELTAT_DECODE_RANGE)-1)

extern const s32 ym_deltat_decode_tableB1[];
extern const s32 ym_deltat_decode_tableB2[];

#define YM_DELTAT_Limit(val,max,min)	\
{					\
	if ( val > max ) val = max;	\
	else if ( val < min ) val = min;\
}
*/
/**** ADPCM B (Delta-T control type) ****/
inline void
YM_DELTAT_ADPCM_CALC (YM_DELTAT * DELTAT)
{
  u32 step;
  int data;
  s32 old_m;
  s32 now_leveling;
  s32 delta_next;

  DELTAT->now_step += DELTAT->step;
  if (DELTAT->now_step >= (1 << YM_DELTAT_SHIFT))
    {
      step = DELTAT->now_step >> YM_DELTAT_SHIFT;
      DELTAT->now_step &= (1 << YM_DELTAT_SHIFT) - 1;
      do
	{
	  if (DELTAT->now_addr > (DELTAT->end << 1))
	    {
	      if (DELTAT->portstate & 0x10)
		{
					/**** repeat start ****/
		  DELTAT->now_addr = DELTAT->start << 1;
		  DELTAT->adpcmx = 0;
		  DELTAT->adpcmd = YM_DELTAT_DELTA_DEF;
		  DELTAT->next_leveling = 0;
		  DELTAT->flag = 1;
		}
	      else
		{
		  DELTAT->arrivedFlag |= DELTAT->flagMask;
		  DELTAT->flag = 0;
		  DELTAT->adpcml = 0;
		  now_leveling = 0;
		  return;
		}
	    }
	  if (DELTAT->now_addr & 1)
	    data = DELTAT->now_data & 0x0f;
	  else
	    {
	      DELTAT->now_data =
		*(ym_deltat_memory + (DELTAT->now_addr >> 1));
	      data = DELTAT->now_data >> 4;
	    }
	  DELTAT->now_addr++;
	  /* shift Measurement value */
	  old_m = DELTAT->adpcmx /*adpcmm */ ;
	  /* ch->adpcmm = YM_DELTAT_Limit( ch->adpcmx + (decode_tableB3[data] * ch->adpcmd / 8) ,YM_DELTAT_DECODE_MAX, YM_DELTAT_DECODE_MIN ); */
	  /* Forecast to next Forecast */
	  DELTAT->adpcmx +=
	    (ym_deltat_decode_tableB1[data] * DELTAT->adpcmd / 8);
	  YM_DELTAT_Limit (DELTAT->adpcmx, YM_DELTAT_DECODE_MAX,
			   YM_DELTAT_DECODE_MIN);
	  /* delta to next delta */
	  DELTAT->adpcmd =
	    (DELTAT->adpcmd * ym_deltat_decode_tableB2[data]) / 64;
	  YM_DELTAT_Limit (DELTAT->adpcmd, YM_DELTAT_DELTA_MAX,
			   YM_DELTAT_DELTA_MIN);
	  /* shift leveling value */
	  delta_next = DELTAT->adpcmx /*adpcmm */  - old_m;
	  now_leveling = DELTAT->next_leveling;
	  DELTAT->next_leveling = old_m + (delta_next / 2);
	}
      while (--step);
/*#define YM_DELTAT_CUT_RE_SAMPLING */
#ifdef YM_DELTAT_CUT_RE_SAMPLING
      DELTAT->adpcml = DELTAT->next_leveling * DELTAT->volume;
      DELTAT->adpcml = DELTAT->adpcmx /*adpcmm */  * DELTAT->volume;
    }
#else
      /* delta step of re-sampling */
      DELTAT->sample_step =
	(DELTAT->next_leveling - now_leveling) * DELTAT->volume_w_step;
      /* output of start point */
      DELTAT->adpcml = now_leveling * DELTAT->volume;
      /* adjust to now */
      DELTAT->adpcml +=
	(int) ((double) DELTAT->sample_step *
	       ((double) DELTAT->now_step / (double) DELTAT->step));
    }
  DELTAT->adpcml += DELTAT->sample_step;
#endif
  /* output for work of output channels (outd[OPNxxxx]) */
  *(DELTAT->pan) += DELTAT->adpcml;
}

#endif /* YM_INLINE_BLOCK */
